package edu.kit.aifb.evtcrawl.twitter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
import org.event_processing.events.types.Event;
import org.event_processing.events.types.TwitterEvent;
import org.ontoware.rdf2go.ModelFactory;
import org.ontoware.rdf2go.RDF2Go;
import org.ontoware.rdf2go.model.Model;
import org.ontoware.rdf2go.model.node.impl.URIImpl;
import org.openrdf.model.Resource;
import org.openrdf.rdf2go.RepositoryModel;
import org.openrdf.rdf2go.RepositoryModelSet;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.n3.N3Writer;
import org.openrdf.sail.nativerdf.NativeStore;

import edu.kit.aifb.evtcrawl.LoadingThread;
import eu.play_project.play_commons.constants.Stream;
import eu.play_project.play_commons.eventtypes.EventHelpers;
import twitter4j.Status;
import twitter4j.StatusDeletionNotice;
import twitter4j.StatusListener;
import static eu.play_project.play_commons.constants.Event.EVENT_ID_SUFFIX;
import static eu.play_project.play_commons.constants.Namespace.EVENTS;
import static edu.kit.aifb.evtcrawl.Configuration.*;


public class TwitterRepositoryHandler implements StatusListener {
	
	private static Random random = new Random();

	private Repository repository;
	private NativeStore nStore;
	private boolean showStream = true;
	ModelFactory modelFactory;
	RepositoryModelSet modelSet;
	ReentrantLock lock;
	
	public void init(){
		try {
			RDF2Go.register(new org.openrdf.rdf2go.RepositoryModelFactory());
			lock = new ReentrantLock();
			modelFactory = RDF2Go.getModelFactory();
			nStore = new NativeStore(new File(REPODIR));
			repository = new SailRepository(nStore);
			repository.initialize();
			modelSet = new RepositoryModelSet(repository);
			modelSet.open();
			
		} catch (RepositoryException e) {
			e.printStackTrace();
		}
	}

	public void onStatus(Status status) {
		lock.lock();
		try {
			if(showStream) System.out.println(status.getUser().getName() + " : " + status.getText());
			Event evt = createTwitterEvent(status);
			if(evt != null) modelSet.addModel(evt.getModel());
		} catch (Exception e) {
			e.printStackTrace();
		}
		finally{
			lock.unlock();
		}
	}
	
	public Event createTwitterEvent(Status status){
		try{
	        // Create an event ID used in RDF context and RDF subject
	        String eventId = EVENTS.getUri() + "twitter" + Math.abs(random.nextLong());
	        Model model = new RepositoryModel(repository);
	        model.open();
	        
	        TwitterEvent event = new TwitterEvent(
	                // set the RDF context part
	        		model,
	                // set the RDF subject
	                eventId + EVENT_ID_SUFFIX,
	                // automatically write the rdf:type statement
	                true);
	         
	        Calendar cal = Calendar.getInstance();
	        cal.setTime(status.getCreatedAt());
	        if (status.getURLEntities() != null){
	            for(int i = 0; i < status.getURLEntities().length; i++) 
	                event.addLinksto(new URIImpl(status.getURLEntities()[i].getURL().toString()));
	        }
	        if (status.getHashtagEntities() != null) {
	            for(int i = 0; i < status.getHashtagEntities().length; i++) event.addHTag(status.getHashtagEntities()[i].getText().toString());
	        }
	        if (status.getUserMentionEntities() != null){
	            for(int i = 0; i < status.getUserMentionEntities().length; i++) event.addUserMention(status.getUserMentionEntities()[i].getScreenName().toString());
	        }
	         
	        event.setEndTime(cal);
	        event.setStream(new URIImpl(Stream.TwitterFeed.getUri()));
	        event.setFriendsCount(status.getUser().getFriendsCount());
	        event.setFollowersCount(status.getUser().getFollowersCount());
	        event.setIsRetweet(status.isRetweet());
	        event.setContent(status.getText());
	        event.setScreenName(status.getUser().getScreenName());
	        event.setTwitterName(status.getUser().getName());
	        if (status.getGeoLocation() != null) {
	            EventHelpers.addLocationToEvent(event, status.getGeoLocation().getLatitude(), status.getGeoLocation().getLongitude());
	        }
	        model.close();
	        return event;
		}
		catch(Exception e){
			e.printStackTrace();
			return null;
		}
    }

	public void serializeRepo(){
		lock.lock();
		FileOutputStream fout;
		try {
			File output = new File("output.n3");
			output.createNewFile();
			fout = new FileOutputStream(output);
			repository.getConnection().export(new N3Writer(fout), (Resource)null);
			fout.close();
			
		} catch (RepositoryException e) {
			e.printStackTrace();
		} catch (RDFHandlerException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		finally{
			lock.unlock();
		}
	}
	
	public void toggleShowStream(){
		showStream = !showStream;
	}
	
	public long getRepoSize(){
		try {
			RepositoryConnection con = repository.getConnection();
			long result = repository.getConnection().size();
			con.close();
			return result;
		} catch (RepositoryException e) {
			e.printStackTrace();
			return -999;
		}
		
	}
	
	public void shutDown(){
		lock.lock();
		try {
			modelSet.close();
			LoadingThread t = new LoadingThread("Shutting down Repository");
			t.start();
			repository.shutDown();
			t.stopRun();
		} catch (Exception e) {
			e.printStackTrace();
		}
		finally{
			lock.unlock();
		}
	}
	
	public void onException(Exception arg0) {}
	public void onDeletionNotice(StatusDeletionNotice arg0) {}
	public void onScrubGeo(long arg0, long arg1) {}
	public void onTrackLimitationNotice(int arg0) {}
	
	
	/*
	public class LoadingThread extends Thread{
		public LoadingThread(String message){
			this.message = message;
		}
		String message;
		public boolean run = true;
		public void stopRun(){run =false;}
		public void run(){
			System.out.print(message);
			while(run){
				System.out.print(".");
				try {
					Thread.sleep(400);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println();
		}
	}*/

}
